package com.stone.db.dbsecurity.interceptor;

import com.alibaba.druid.util.JdbcConstants;
import com.stone.db.dbsecurity.SQLParse;
import com.stone.db.dbsecurity.SQLParseResult;
import com.stone.db.dbsecurity.constant.SymbolConstant;
import com.stone.db.dbsecurity.helper.InterceptorHelper;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Properties;

/**
 * Created by stone on 2017/11/29.
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SecurityInterceptor implements BaseInterceptor {

    /**
     * SQL分析结果
     */
    private SQLParse sqlParse;

    private InterceptorHelper interceptorHelper = new InterceptorHelper();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result;
        try {
            final Executor executor = (Executor) invocation.getTarget();
            final Object[] args = invocation.getArgs();

            MetaObject metaStatementHandler = SystemMetaObject.forObject(executor);
            // 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过循环可以分离出最原始的的目标类)
            while (metaStatementHandler.hasGetter("h")) {
                Object metaStatementHandlerValue = metaStatementHandler.getValue("h");
                metaStatementHandler = SystemMetaObject.forObject(metaStatementHandlerValue);
            }

            MappedStatement mappedStatement = (MappedStatement) args[0];

            final Object insertParam = args[1];

            final SqlSource sqlSource = mappedStatement.getSqlSource();

            final BoundSql boundSql = sqlSource.getBoundSql(insertParam);

            String sql = boundSql.getSql();

            final SQLParseResult sqlParseResult = this.sqlParse.parseSQL(sql);

            String tableName = sqlParseResult.getTableName();

            if (!interceptorHelper.isAllowTable(tableName)) {
                return invocation.proceed();
            }
            result = interceptorHelper.getResult(invocation, mappedStatement, insertParam,tableName);
        } catch (Exception e) {
            result = invocation.proceed();
        }
        return result;
    }

    @Override
    public Object plugin(Object object) {
        return Plugin.wrap(object, this);
    }

    @Override
    public void setProperties(Properties pro) {
        this.interceptorHelper.parseProperties(pro);
        this.sqlParse = new SQLParse(pro.getProperty(SymbolConstant.DB_TYPE, JdbcConstants.MYSQL));
    }


}
